package com.yangchoi.jetpackcompose.ui.widget.component

import androidx.compose.foundation.layout.*
import androidx.compose.material.Divider
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.animation.core.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.IntOffset
import com.yangchoi.jetpackcompose.R
import com.yangchoi.jetpackcompose.theme.black_12Sp
import com.yangchoi.jetpackcompose.theme.black_16Sp
import com.yangchoi.jetpackcompose.theme.cA6A6A6_12sp
import com.yangchoi.jetpackcompose.ui.widget.ext.onClick
import kotlinx.coroutines.launch
import java.util.Calendar
import kotlin.math.abs
import kotlin.math.roundToInt


val cal: Calendar = Calendar.getInstance()
val currentYear: Int = cal.get(Calendar.YEAR)
val currentMonth: Int = cal.get(Calendar.MONTH) + 1 // 注意月份是从 0 开始计数的，需要加上 1 才能得到正确的值
val currentDay: Int = cal.get(Calendar.DAY_OF_MONTH)
val currentHour:Int = cal.get(Calendar.HOUR_OF_DAY)
val currentMinute:Int = cal.get(Calendar.MINUTE)
val currentSecond:Int = cal.get(Calendar.SECOND)
/**
 * 日期选择器
 * @param year 年
 * @param month 月
 * @param day 日
 * @param hour 时
 * @param minute 分
 * @param second 秒
 * @param onlyTime 是否只显示时分秒
 * @param onDismiss 回调方法
 */
@Composable
fun DatePicker(
    year: Int = currentYear,
    month: Int = currentMonth,
    day: Int = currentDay,
    hour:Int = currentHour,
    minute:Int = currentMinute,
    second:Int = currentSecond,
    onlyTime:Boolean = false,
    onDismiss: (selected: Boolean, onlyTime:Boolean, year: Int, month: Int, day: Int,hour:Int,minute:Int,second:Int) -> Unit
) {
    var selectYear by remember { mutableStateOf(year) }
    var selectMonth by remember { mutableStateOf(month) }
    val selectDay = remember { mutableStateOf(day) }
    var selectHour by remember { mutableStateOf(hour) }
    var selectMinute by remember { mutableStateOf(minute) }
    var selectSecond by remember { mutableStateOf(second) }

    Column(
        Modifier
            .fillMaxWidth()
            .padding(top = 4.dp),
    ) {

        Row(modifier = Modifier
            .padding(horizontal = 20.dp, vertical = 10.dp)
            .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
            Text(text = "取消", style = cA6A6A6_12sp, modifier = Modifier.clickable(
                indication = null,
                interactionSource = remember { MutableInteractionSource() },
                onClick = {
                    onDismiss(false, onlyTime,0, 0, 0, 0, 0, 0)
                }
            ))
            Text(text = "选择时间", style = black_16Sp)
            Text(text = "确定", style = black_12Sp, modifier = Modifier.clickable(
                indication = null,
                interactionSource = remember { MutableInteractionSource() },
                onClick = {
                    onDismiss(true,onlyTime, selectYear, selectMonth, selectDay.value,selectHour,selectMinute,selectSecond)
                }
            ))
        }

        DateWheel(selectYear, selectMonth, selectDay,selectHour,selectMinute,selectSecond,onlyTime) { index, value ->
            when (index) {
                0 -> selectYear = value
                1 -> selectMonth = value
                2 -> selectDay.value = value
                3 -> selectHour = value
                4 -> selectMinute = value
                5 -> selectSecond = value
            }
        }
    }
}

@Composable
fun DatePickerYearAndMonth(
    year: Int = currentYear,
    month: Int = currentMonth,
    onDismiss: (selected: Boolean, year: Int, month: Int) -> Unit
) {
    var selectYear by remember { mutableStateOf(year) }
    var selectMonth by remember { mutableStateOf(month) }

    Column(
        Modifier
            .fillMaxWidth()
            .padding(top = 4.dp),
    ) {

        Image(painter = painterResource(id = R.mipmap.icon_loading),
            contentDescription = null,
            modifier = Modifier
                .padding(top = 10.dp, bottom = 10.dp, end = 20.dp)
                .align(Alignment.End)
                .size(15.dp)
                .onClick { onDismiss(false, 0, 0) }
        )

        DateWheelYearAndMonth(selectYear, selectMonth) { index, value ->
            when (index) {
                0 -> selectYear = value
                1 -> selectMonth = value
            }
        }
        CommonButton(btnTitle = "确定"){
            onDismiss(true, selectYear, selectMonth)
        }
    }
}

/**
 * 时间选择器 - 睡眠 - (开始-结束时间)
 */
@Composable
private fun DateWheel(
    year: Int,
    month: Int,
    day: MutableState<Int>,
    hour:Int = 0,
    minute:Int = 0,
    second:Int = 0,
    onlyTime: Boolean,
    onChange: (index: Int, value: Int) -> Unit
) {
    Box(
        modifier = Modifier.fillMaxWidth(),
        Alignment.Center
    ) {
        Row(
            Modifier
                .fillMaxWidth()
                .padding(horizontal = 12.dp)) {
            val modifier = Modifier.weight(1f)

            if(!onlyTime){
                //  年
                ColumnPicker(
                    modifier = modifier,
                    value = year,
                    label = { "${it}年" },
                    range = 1920..2060,
                    onValueChange = {
                        onChange(0, it)
                    }
                )
                //  月
                ColumnPicker(
                    modifier = modifier,
                    value = month,
                    label = { "${it}月" },
                    range = 1..12,
                    onValueChange = {
                        onChange(1, it)
                    }
                )

                //  日
                val lastDay = getLastDay(year, month)
                if (day.value > lastDay) day.value = lastDay
                ColumnPicker(
                    modifier = modifier,
                    value = day.value,
                    label = { "${it}日" },
                    range = 1..lastDay,
                    onValueChange = {
                        onChange(2, it)
                    }
                )
            }

            // 时
            ColumnPicker(
                modifier = modifier,
                value = hour,
                label = { if (it < 10 ) "0$it" else "$it" },
                range = 0..23,
                onValueChange = {
                    onChange(3, it)
                }
            )

            // 分
            ColumnPicker(
                modifier = modifier,
                value = minute,
                label = { if (it < 10 ) "0$it" else "$it" },
                range = 0..59,
                onValueChange = {
                    onChange(4, it)
                }
            )

            // 秒
            ColumnPicker(
                modifier = modifier,
                value = second,
                label = { if (it < 10 ) "0$it" else "$it" },
                range = 0..59,
                onValueChange = {
                    onChange(5, it)
                }
            )
        }

        // 中间两道横线
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(38.dp)
                .align(Alignment.Center)
        ) {
            Divider(Modifier.padding(horizontal = 15.dp))
            Divider(
                Modifier
                    .padding(horizontal = 15.dp)
                    .align(Alignment.BottomStart)
            )
        }
    }
}

@Composable
private fun DateWheelYearAndMonth(
    year: Int,
    month: Int,
    onChange: (index: Int, value: Int) -> Unit
) {
    Box(
        modifier = Modifier.fillMaxWidth(),
        Alignment.Center
    ) {
        Row(
            Modifier
                .fillMaxWidth()
                .padding(horizontal = 12.dp)) {
            val modifier = Modifier.weight(1f)
            //  年
            ColumnPicker(
                modifier = modifier,
                value = year,
                label = { "${it}年" },
                range = 1920..2060,
                onValueChange = {
                    onChange(0, it)
                }
            )
            //  月
            ColumnPicker(
                modifier = modifier,
                value = month,
                label = { "${it}月" },
                range = 1..12,
                onValueChange = {
                    onChange(1, it)
                }
            )
        }

        // 中间两道横线
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(38.dp)
                .align(Alignment.Center)
        ) {
            Divider(Modifier.padding(horizontal = 15.dp))
            Divider(
                Modifier
                    .padding(horizontal = 15.dp)
                    .align(Alignment.BottomStart)
            )
        }
    }
}

/**
 * 根据年月, 获取天数
 */
private fun getLastDay(year: Int, month: Int): Int {
    return when (month) {
        1, 3, 5, 7, 8, 10, 12 -> 31
        4, 6, 9, 11 -> 30
        else -> {
            // 百年: 四百年一闰年;  否则: 四年一闰年;
            if (year % 100 == 0) {
                if (year % 400 == 0) {
                    29
                } else {
                    28
                }
            } else {
                if (year % 4 == 0) {
                    29
                } else {
                    28
                }
            }
        }
    }
}

/**
 * 条目高度.
 */
private val itemHeight = 38.dp

/**
 * 透明度控制, 每条目距离 差距
 */
private const val itemAlphaGap = 0.3f

/**
 * 条目数量. 这个暂时不能随便改.  目前得同时改逻辑才行;
 */
private const val columnCount = 5

/**
 * 数字滚轮 Picker
 */
@Composable
fun ColumnPicker(
    modifier: Modifier = Modifier,
    label: (Int) -> String = {
        it.toString()
    },
    value: Int,
    onValueChange: (Int) -> Unit,
    range: Iterable<Int>,
    textStyle: TextStyle = LocalTextStyle.current,
) {
    ListItemPicker(
        modifier = modifier,
        label = label,
        value = value,
        onValueChange = onValueChange,
        list = range.toList(),
        textStyle = textStyle
    )
}


@Composable
fun <T> ListItemPicker(
    modifier: Modifier = Modifier,
    label: (T) -> String = { it.toString() },
    value: T,
    onValueChange: (T) -> Unit,
    list: List<T>,
    textStyle: TextStyle = LocalTextStyle.current,
) {
    val columnHeightPx = with(LocalDensity.current) { itemHeight.toPx() }

    val coroutineScope = rememberCoroutineScope()

    val animatedOffset = remember { Animatable(0f) }
        .apply {
            val index = list.indexOf(value)
            val offsetRange = remember(value, list) {
                -((list.count() - 1) - index) * columnHeightPx to
                        index * columnHeightPx
            }
            updateBounds(offsetRange.first, offsetRange.second)
        }
    val coercedAnimatedOffset = animatedOffset.value % columnHeightPx
    val currentIndex = getItemIndexForOffset(list, value, animatedOffset.value, columnHeightPx)

    Layout(
        modifier = modifier
            .draggable(
                orientation = Orientation.Vertical,
                state = rememberDraggableState { deltaY ->
                    coroutineScope.launch {
                        animatedOffset.snapTo(animatedOffset.value + deltaY)
                    }
                },
                onDragStopped = { velocity ->
                    coroutineScope.launch {
                        val endValue = animatedOffset.fling(
                            initialVelocity = velocity,
                            animationSpec = exponentialDecay(frictionMultiplier = 20f),
                            adjustTarget = { target ->
                                val coercedTarget = target % columnHeightPx
                                val coercedAnchors =
                                    listOf(-columnHeightPx, 0f, columnHeightPx)
                                val coercedPoint = coercedAnchors.minByOrNull { abs(it - coercedTarget) }!!
                                val base = columnHeightPx * (target / columnHeightPx).toInt()
                                coercedPoint + base
                            }
                        ).endState.value

                        val result = list.elementAt(
                            getItemIndexForOffset(list, value, endValue, columnHeightPx)
                        )
                        onValueChange(result)
                        animatedOffset.snapTo(0f)
                    }
                }
            ),
        content = {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(itemHeight * columnCount)
                    .offset { IntOffset(x = 0, y = coercedAnimatedOffset.roundToInt()) }
            ) {
                val baseLabelModifier = Modifier.align(Alignment.Center)
                ProvideTextStyle(textStyle) {
                    for (index in (currentIndex-3)..(currentIndex+3)) {
                        if(index >= 0 && index < list.size) {
                            val off = index - currentIndex
                            val absOff = abs(off)
                            val half = columnHeightPx / 2f

                            // coercedAnimatedOffset: 下拉为正, 上推为负;
                            when{
                                off == -3 && coercedAnimatedOffset > half -> {
                                    // 下拉, 超过一半, -3要提前出现;
                                    val alpha = getAlpha(absOff, -coercedAnimatedOffset, columnHeightPx)
                                    Label(
                                        text = label(list.elementAt(index)),
                                        modifier = baseLabelModifier
                                            .offset(y = itemHeight * off)
                                            .alpha(alpha),
                                    )
                                }
                                off == -2
                                        && ((coercedAnimatedOffset >= 0) || (coercedAnimatedOffset < 0 && coercedAnimatedOffset > -half))
                                -> {
                                    //  下拉 offset >= 0 则必定显示;  上推, 不超过一半. -2则出现
                                    val alpha = getAlpha(absOff, -coercedAnimatedOffset, columnHeightPx)
                                    Label(
                                        text = label(list.elementAt(index)),
                                        modifier = baseLabelModifier
                                            .offset(y = itemHeight * off)
                                            .alpha(alpha),
                                    )
                                }
                                off == 3 && coercedAnimatedOffset < -half -> {
                                    // 上推, 超过半个条目. 3则出现;
                                    val alpha = getAlpha(absOff, coercedAnimatedOffset, columnHeightPx)
                                    Label(
                                        text = label(list.elementAt(index)),
                                        modifier = baseLabelModifier
                                            .offset(y = itemHeight * off)
                                            .alpha(alpha),
                                    )
                                }
                                off == 2
                                        && ((coercedAnimatedOffset <= 0) || (coercedAnimatedOffset > 0 && coercedAnimatedOffset < half))
                                -> {
                                    // 上推 offset <=0 则必定显示;  下拉, 不超过半个条目. 2则出现;
                                    val alpha = getAlpha(absOff, coercedAnimatedOffset, columnHeightPx)
                                    Label(
                                        text = label(list.elementAt(index)),
                                        modifier = baseLabelModifier
                                            .offset(y = itemHeight * off)
                                            .alpha(alpha),
                                    )
                                }
                                absOff == 1 -> { // -1, 1
                                    val alpha = getAlpha(absOff, coercedAnimatedOffset, columnHeightPx)
                                    Label(
                                        text = label(list.elementAt(index)),
                                        modifier = baseLabelModifier
                                            .offset(y = itemHeight * off)
                                            .alpha(alpha),
                                    )
                                }
                                off == 0 -> {
                                    Label(
                                        text = label(list.elementAt(index)),
                                        modifier = baseLabelModifier,
                                        color = MaterialTheme.colors.primary,
                                    )
                                }
                            } // when
                        }
                    }
                }
            }
        }
    ) { measurables, constraints ->
        val placeables = measurables.map { measurable ->
            measurable.measure(constraints)
        }

        layout(constraints.maxWidth, (itemHeight*columnCount).toPx().toInt()) {
            var yPosition = 0
            placeables.forEach { placeable ->
                placeable.placeRelative(x = 0, y = yPosition)
                yPosition += placeable.height
            }
        }
    }
}

/**
 * 根据索引, 以及偏移量 计算透明度;
 * 每级阶差 0.3f
 */
private fun getAlpha(offIndex: Int, offset: Float, columnHeight: Float) =
    1 - offIndex * itemAlphaGap - (offset / columnHeight) * itemAlphaGap


/**
 * 文字,居中. 高度固定为 itemHeight;
 * @param text 文字
 * @param color 文字颜色
 */
@Composable
private fun Label(
    text: String,
    modifier: Modifier,
    color: Color = Color.Black,
) {
    Box(
        modifier = modifier
            .height(itemHeight)
    ) {
        Text(text = text, modifier = Modifier.align(Alignment.Center), color = color)
    }
}

private suspend fun Animatable<Float, AnimationVector1D>.fling(
    initialVelocity: Float,
    animationSpec: DecayAnimationSpec<Float>,
    adjustTarget: ((Float) -> Float)?,
    block: (Animatable<Float, AnimationVector1D>.() -> Unit)? = null,
): AnimationResult<Float, AnimationVector1D> {
    val targetValue = animationSpec.calculateTargetValue(value, initialVelocity)
    val adjustedTarget = adjustTarget?.invoke(targetValue)
    return if (adjustedTarget != null) {
        animateTo(
            targetValue = adjustedTarget,
            initialVelocity = initialVelocity,
            block = block
        )
    } else {
        animateDecay(
            initialVelocity = initialVelocity,
            animationSpec = animationSpec,
            block = block,
        )
    }
}

private fun <T> getItemIndexForOffset(
    range: List<T>,
    value: T,
    offset: Float,
    halfNumbersColumnHeightPx: Float
): Int {
    val indexOf = range.indexOf(value) - (offset / halfNumbersColumnHeightPx).toInt()
    return maxOf(0, minOf(indexOf, range.count() - 1))
}

